package com.example.testdemo.kinetic.sprite.bullet;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.andengine.entity.sprite.Sprite;
import org.andengine.opengl.texture.region.ITiledTextureRegion;

import android.util.SparseArray;

import com.example.testdemo.TankGameActivity;
import com.example.testdemo.kinetic.data.DataOfBullet1;
import com.example.testdemo.kinetic.interfaces.IEnergyStorable;
import com.example.testdemo.kinetic.interfaces.IOnDestroyTargetListener;
import com.example.testdemo.kinetic.sprite.KineticSprite;
import com.example.testdemo.kinetic.sprite.tank.enemy.EnemyTank1;
import com.example.testdemo.kinetic.sprite.tank.player.PlayerTank1;
import com.example.testdemo.pools.BulletPools;

public abstract class BaseBullet extends KineticSprite implements
        IEnergyStorable {
	@SuppressWarnings("unused")
	private final String TAG = BaseBullet.class.getSimpleName();

	public interface ObjectType {
		public int PLAYER = 0;
		public int ENEMY = 1;
		public int WALL = 2;
		public int OBSTACLE = 3;
		public int HEART = 4;
		public int PLAYER_BULLET = 5;
		public int ENEMY_BULLET = 6;
	}

	private final float EXPANSION_FACTOR = 0.3f;
	private int mEnergyLevel = 0;

	protected BulletPools mBulletPools = null;
	public String mOwner = null;
	public DataOfBullet1 mData = null;

	// <name, listener>
	protected Map<String, IOnDestroyTargetListener> mListenerMap = new HashMap<String, IOnDestroyTargetListener>();

	protected SparseArray<List<Sprite>> mPredictCollisionList = new SparseArray<List<Sprite>>();

	public BaseBullet(BulletPools bulletPools, int whichScene, String owner,
	        String pName, float pX, float pY,
	        ITiledTextureRegion pTiledTextureRegion, TankGameActivity pActivity) {
		super(whichScene, pName, pX, pY, pTiledTextureRegion, pActivity);
		mBulletPools = bulletPools;
		mOwner = owner;
		mData = new DataOfBullet1(DataOfBullet1.STRENTH_1, DataOfBullet1.SPEED_1);
	}
	
	@Override
	protected void resetSpriteData() {
		mData.reset();
	}

	public void registerDestroyTargetListener(String who,
	        IOnDestroyTargetListener listener) {
		if (!mListenerMap.containsKey(who)) {
			mListenerMap.put(who, listener);
		}
	}

	public void fire(KineticSprite owner) {
		final float pX = owner.getX();
		final float pY = owner.getY();
		final float direction = owner.getRotation();

		float centerX = 0, centerY = 0;
		float xVelocity = 0, yVelocity = 0;
		float FACE_DOWN = 0, FACE_UP = 0, FACE_RIGHT = 0, FACE_LEFT = 0;

		if (owner instanceof PlayerTank1) {
			FACE_DOWN = PlayerTank1.FACE_DOWN;
			FACE_UP = PlayerTank1.FACE_UP;
			FACE_RIGHT = PlayerTank1.FACE_RIGHT;
			FACE_LEFT = PlayerTank1.FACE_LEFT;
		} else if (owner instanceof EnemyTank1) {
			FACE_DOWN = EnemyTank1.FACE_DOWN;
			FACE_UP = EnemyTank1.FACE_UP;
			FACE_RIGHT = EnemyTank1.FACE_RIGHT;
			FACE_LEFT = EnemyTank1.FACE_LEFT;
		}

		if (direction == FACE_DOWN) {
			centerX = pX + owner.getWidth() / 2 - getWidth() / 2;
			centerY = pY + owner.getHeight();
			setRotation(Bullet1.FACE_DOWN);
			yVelocity = 1;
		} else if (direction == FACE_UP) {
			centerX = pX + owner.getWidth() / 2 - getWidth() / 2;
			centerY = pY;
			setRotation(Bullet1.FACE_UP);
			yVelocity = -1;
		} else if (direction == FACE_RIGHT) {
			centerX = pX + owner.getWidth();
			centerY = pY + owner.getHeight() / 2 - getHeight() / 2;
			setRotation(Bullet1.FACE_RIGHT);
			xVelocity = 1;
		} else if (direction == FACE_LEFT) {
			centerX = pX;
			centerY = pY + owner.getHeight() / 2 - getHeight() / 2;
			setRotation(Bullet1.FACE_LEFT);
			xVelocity = -1;
		}

		setPosition(centerX, centerY);

		// Add forecast collisions
		// due to only can call one time fire() in bullet visible phase,
		// so we reset the mListForecastCollisions list in fire()
		generatePredictCollisionList();

		float speed = mData.getMoveSpeed();
		if (this instanceof Bullet1) {
			speed = reCalcSpeed();
		}
		setVelocity(speed * xVelocity, speed * yVelocity);

		mCharacterResources.mSoundShoot.play();
	}

	@Override
	protected void onManagedUpdate(float pSecondsElapsed) {
		super.onManagedUpdate(pSecondsElapsed);

		if (isVisible()) {
			if (resolvedWallCollision(mPredictCollisionList
			        .get(ObjectType.WALL))) {
				return;
			}

			if (resolvedObstacleCollision(mPredictCollisionList
			        .get(ObjectType.OBSTACLE))) {
				return;
			}
			
			if (mOwner.startsWith(PlayerTank1.BASE_NAME)
					&& resolvedEnemyBulletCollision(mPredictCollisionList.get(ObjectType.ENEMY_BULLET))) {
				return;
			}

			if (mOwner.startsWith(PlayerTank1.BASE_NAME)
			        && resolvedEnemyCollision(mPredictCollisionList
			                .get(ObjectType.ENEMY))) {
				return;
			}

			if (mOwner.startsWith(EnemyTank1.BASE_NAME)
			        && resolvedPlayerCollision(mPredictCollisionList
			                .get(ObjectType.PLAYER))) {
				return;
			}
			
			if (resolvedHeartCollision(mPredictCollisionList
			        .get(ObjectType.HEART))) {
				return;
			}
		}
	}

	protected abstract void generatePredictCollisionList();

	protected abstract boolean resolvedWallCollision(List<Sprite> wallList);

	protected abstract boolean resolvedObstacleCollision(
	        List<Sprite> obstacleList);

	// Only need check player bullet whether collidesWith enemy's
	protected abstract boolean resolvedEnemyBulletCollision(List<Sprite> bulletList);
	
	protected abstract boolean resolvedEnemyCollision(List<Sprite> enemyList);

	protected abstract boolean resolvedPlayerCollision(List<Sprite> playerList);

	protected abstract boolean resolvedHeartCollision(List<Sprite> heartList);

	protected abstract boolean fastCollidesWith(Sprite target);

	public void free() {
		mListenerMap.clear();
	}

	// IEnergyStorable
	@Override
	public void setEnergyLevel(int level) {
		if (level >= IEnergyStorable.MIN_LEVEL
		        && level <= IEnergyStorable.MAX_LEVEL) {
			mEnergyLevel = level;
			setScale(1 + (mEnergyLevel * EXPANSION_FACTOR));
			mData.setStrength(reCalcStrength());
		}
	}

	@Override
	public void levelUpEnergy() {
		if (mEnergyLevel >= IEnergyStorable.MIN_LEVEL
		        && mEnergyLevel < IEnergyStorable.MAX_LEVEL) {
			mEnergyLevel++;
			setScale(1 + (mEnergyLevel * EXPANSION_FACTOR));
			mData.setStrength(reCalcStrength());
		}
	}

	@Override
	public void clearEnergy() {
		setEnergyLevel(IEnergyStorable.LEVEL_0);
	}

	@Override
	public int getEnergyLevel() {
		return mEnergyLevel;
	}

	private int reCalcStrength() {
		if (this instanceof Bullet1) {
			if (mEnergyLevel >= IEnergyStorable.LEVEL_4) {
				return mData.getStrength() + 2;
			} else if (mEnergyLevel >= IEnergyStorable.LEVEL_2) {
				return mData.getStrength() + 1;
			}
		}

		return mData.getStrength();
	}

	private float reCalcSpeed() {
		float speed = 0;
		if (this instanceof Bullet1) {
			if (mEnergyLevel >= IEnergyStorable.LEVEL_4) {
				speed = mData.getMoveSpeed() * 2.0f;
			} else if (mEnergyLevel >= IEnergyStorable.LEVEL_2) {
				speed = mData.getMoveSpeed() * 1.5f;
			}
		}

		if (speed > 0) {
			if (speed > DataOfBullet1.SPEED_MAX) {
				speed = DataOfBullet1.SPEED_MAX;
			}
			return speed;
		} else {
			return mData.getMoveSpeed();
		}
	}
}
